前端那些事

vuePress-theme-reco chenpeng    2020 - 2021
前端那些事 前端那些事

Choose mode

  • dark
  • auto
  • light
首页
文章目录
  • Browser
  • CSS
  • ES6
  • JavaScript
  • Network
  • TypeScript
  • Vue
  • Vue3
  • Webpack
标签
时间轴
GitHub
author-avatar

chenpeng

85

Article

25

Tag

首页
文章目录
  • Browser
  • CSS
  • ES6
  • JavaScript
  • Network
  • TypeScript
  • Vue
  • Vue3
  • Webpack
标签
时间轴
GitHub
  • CSS

    • CSS Modules
    • CSS层叠上下文
    • CSS中的伪类与伪元素
    • CSS水平垂直居中
    • CSS两栏布局和三栏布局
    • CSS - BFC
    • link与@import的区别
    • CSS动画相关属性
    • CSS - flex

CSS Modules

vuePress-theme-reco chenpeng    2020 - 2021

CSS Modules

chenpeng 2020-11-29 CSS

# 1、什么是 CSS Modules?

所有的 class 的名称和动画的名称默认属于本地作用域的 CSS 文件。所以 CSS Modules 不是一个官方的规范,也不是浏览器的一种机制,它是一种构建步骤中的一个进程(构建通常需要 webpack 或者 browserify 的帮助),通过构建工具的帮助,可以将 class 的名字或者选择器的名字作用域化(类似命名空间化),通过构建工具来使指定class 达到 scope 的过程。

# 2、为什么引入 CSS Modules?

  1. 全局样式冲突

    webpack 进行打包时,将所有js文件导入到入口App.js文件中,样式也会统一加载到入口中,根据css的layout规则,后面的样式会覆盖掉前面的样式声明,造成全局样式的覆盖问题。

  2. 嵌套层次过深的选择器

    为了解决全局样式的冲突问题,不得不引入一些特地命名namespace来区分,但是往往有些namespace命名得不够清晰,就会造成要想下一个样式不会覆盖,就要再加一个新的命名空间来进行区分,最终可能一个元素的显示样式嵌套特别深。

    嵌套层次过深会造成的问题:

    • 根据CSS选择器的解析规则可以知道,层级越深,比较的次数也就越多,影响整个页面的渲染
    • 增加了不必要的字节开销
    • 语义混乱 可扩展性不好,约束越多,扩展性越差
  3. 无法共享变量

    复杂组件要使用 JS 和 CSS 来共同处理样式,就会造成有些变量在JavaScript和CSS中冗余

  4. 代码冗余,难以维护

# 3、CSS Modules 的优势

  • 解决全局命名冲突问题,CSS Modules 只关心组件本身 命名唯一
  • 模块化可以使用 composes 来引入自身模块中的样式以及另一个模块的样式
  • 解决嵌套层次过深的问题,使用扁平化的类名

# 4、如何使用 CSS Modules

创建一个初始项目

├── build
│   └── built.js
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── src
│   ├── index.js
│   └── css
└── webpack.config.js
1
2
3
4
5
6
7
8
9
10

index.js 作为程序入口,css 文件夹存放样式文件,webpack.config.js 作为 webpack 的配置文件

index.css

.title{
    font-size: 20px;
    font-weight: bold;
}
1
2
3
4

index.js

// 通过对象的形式引入 css
import Index from '../css/index.css'

const html = `<h1 class="${Index.title}">CSS Modules</h1>`

document.querySelector('body').innerHTML = html
1
2
3
4
5
6

webpack.config.js

设置 css-loader 的 modules 属性为 true 开启 CSS Modules 功能

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build'),
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // 用这个loader取代style-loader,将js中的css提取成单独文件
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            publicPath: '../'
                        }
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    }
                ],
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/main.css'
        })
    ],
    mode: 'development'
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 5、CSS Modules 作用域

  1. 全局作用域

    使用 :global(.className) 的语法,声明一个全局规则,这样声明的 class,都不会被编译成哈希字符串

  2. 局部作用域

    :local(.className) 等同于 .className

# 6、定制哈希类名

css-loader 默认的哈希算法是 [hash:base64],这会将 .title 编译成 ._3cgV55LcqGee3dnKitPjdl 这样的字符串

在 webpack.config.js 中可以定制哈希字符串的格式

{
    loader: 'css-loader',
        options: {
            modules: {
                localIdentName: '[path][name]--[local]--[hash:base64:5]'
            }
        }
}
1
2
3
4
5
6
7
8

这会将 .title 编译成 .src-css-index--title--3cgV5

# 7、class 的组合

在 CSS Modules 中,一个选择器可以继承另一个选择器的规则

例如,在 index.css 中让 .title 继承 .red,使用 composes

.red{
    color: red;
}

.title{
    composes: red;
    font-size: 20px;
    font-weight: bold;
}
1
2
3
4
5
6
7
8
9

# 8、引入其他模块

选择器也可以继承其他 CSS 文件里的规则

color.css

.blue{
    color: dodgerblue;
}
1
2
3

index.css

.red{
    color: red;
}

.title{
    composes: blue from './color.css';
    font-size: 20px;
    font-weight: bold;
}
1
2
3
4
5
6
7
8
9

css 文件自身继承和从其他文件引入的相同申明优先级:

.red{
    color: red;
}

.title{
    composes: red;
    composes: blue from './color.css';
    font-size: 20px;
    font-weight: bold;
}
1
2
3
4
5
6
7
8
9
10

自身的优先级更高

# 9、使用变量

CSS Modules 支持使用变量,需要安装 PostCSS 和 postcss-modules-values

在 webpack.config.js 中引入 postcss-modules-values 并配置 postcss-loader

const values = require('postcss-modules-values');
...
{
    loader: 'postcss-loader',
        options: {
            postcss: [
                values
            ]
        }
}
1
2
3
4
5
6
7
8
9
10

在 index.css 中定义变量并使用

@value dark-blue: #4e6ef2;

.title{
    color: dark-blue;
}
1
2
3
4
5